#ifndef ANALYSISRESULT_H
#define ANALYSISRESULT_H
#include <iostream>
#include "TH3D.h"
#include "TCanvas.h"
#include "TKey.h"
#include <math.h>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <list>
#include "IOmanager.h"
#include "ResultTTreeConfig.h"

using namespace std;
using namespace boost;

class AnalysisResult {
    public:
        class FitResult {
            private:
                AnalysisResult * parent;
                string prefix;
                template <typename T>
                void setParameter(string param, T value) {
                    cout << param << endl;
                    parent->setParameter(param,value);
                }
                template <typename T>
                void setTemplateParameter(string param, int num, T value) {
                    setParameter(prefix+"_"+param+boost::lexical_cast<string>(num),value);
                }
            public:
                FitResult(AnalysisResult * parent, string prefix) {
                    this->parent=parent;
                    this->prefix=prefix;
                }
                void setBgdCount(int num, double value) {
                    setTemplateParameter("nbgd",num,value);
                }
                void setSigCount(int num, double value) {
                    setTemplateParameter("nsig",num,value);
                }
                void setBgdError(int num, double value) {
                    setTemplateParameter("nbgd_err",num,value);
                }
                void setSigError(int num, double value) {
                    setTemplateParameter("nsig_err",num,value);
                }
                void setSigBelowIsolTh(int num, double value) {
                    cout << "setSigBelowIsolTh: " << value << endl;
                    setTemplateParameter("nsig_isolth",num,value);
                }
                void setBgdBelowIsolTh(int num, double value) {
                    cout << "setBgdBelowIsolTh: " << value << endl;
                    setTemplateParameter("nbgd_isolth",num,value);
                }
                void setFitChiSq(double value) {
                    setParameter("fit_chi_sq",value);
                }
        };
    private:

        map <string,int> intResult;
        map <string,double> doubleResult;
        map <string,string> stringResult;
        /** We disallow copyconstruction of any kind */
        AnalysisResult(AnalysisResult const &a);
        AnalysisResult & operator= (AnalysisResult const &a);
        TTree * ntuple;
        template <typename T>
            void setTemplateParameter(string param, int num, T value) {
                setParameter(param+boost::lexical_cast<string>(num),value);
            }
        void setParameter(string param, int value) {
            intResult[param]=value;
        }
        void setParameter(string param, double value) {
            doubleResult[param]=value;
        }
        void setParameter(string param, string value) {
            //stringResult.push_back(value);
            /// WARNING: CRAZY DANGEROUS: DEPENDENCY ON UNCHANGED c_str()
            //ntuple->Branch(param.c_str(),stringResult.back().c_str());
        }
        void init() {
            /// initialize variables that need defaults
            probe.setFitChiSq(0.0);
            selected.setFitChiSq(0.0);
            for(int i = 1;i<=ResultTTreeConfig::maxBgdProbe;i++) {
                setBgdProbeTemplate(i,0);
                probe.setBgdCount(i,0.0);
                probe.setBgdError(i,0.0);
            }
            for(int i = 1;i<=ResultTTreeConfig::maxBgdSelected;i++) {
                setBgdSelectedTemplate(i,0);
                selected.setBgdCount(i,0.0);
                selected.setBgdError(i,0.0);
            }
            for(int i = 1;i<=ResultTTreeConfig::maxSigProbe;i++) {
                setSigProbeTemplate(i,0);
                probe.setSigCount(i,0.0);
                probe.setSigError(i,0.0);
            }
            for(int i = 1;i<=ResultTTreeConfig::maxSigSelected;i++) {
                setSigSelectedTemplate(i,0);
                selected.setSigCount(i,0.0);
                selected.setSigError(i,0.0);
            }
        }
    public:
        FitResult probe, selected;
        AnalysisResult() : probe(this,"probe"),selected(this,"selected") {
            //ntuple = boost::make_shared<TTree>("Results","Results");
            ntuple = IOmanager::instance().ntuple;
            init();
        }

        void save() {
            //boost::shared_ptr<TFile> output = boost::make_shared<TFile>(filename, mode);
            //ntuple->ResetBranchAddresses();
            for(map <string,int>::iterator it = intResult.begin();it!=intResult.end();it++) {
                TBranch * branch = ntuple->GetBranch(it->first.c_str());
                if(branch==NULL) branch = ntuple->Branch(it->first.c_str(),&it->second,(it->first+"/I").c_str());
                branch->SetAddress(&it->second);
            }
            for(map <string,double>::iterator it = doubleResult.begin();it!=doubleResult.end();it++) {
                TBranch * branch = ntuple->GetBranch(it->first.c_str());
                if(branch==NULL) branch = ntuple->Branch(it->first.c_str(),&it->second,(it->first+"/D").c_str());
                branch->SetAddress(&it->second);
            }
            ntuple->Fill();
            //IOmanager::instance().save(ntuple);
            //output->WriteObject(ntuple);
            //output->Close();
        }

        void setVersion(int value) {
            setParameter("version", value);
        }
        void setIsData(int value) {
            setParameter("isData", value);
        }
        void setMethod(int value) {
            setParameter("method", value);
        }
        void setEfficCalcMethod(int value) {
            setParameter("effic_method", value);
        }
        void setETAMin(int value) {
            setParameter("eta_min", value);
        }
        void setETAMax(int value) {
            setParameter("eta_max", value);
        }
        void setSigProbeTemplate(int num, int value) {
            cout << "sigprobe" << num << " " << value << endl;
            setTemplateParameter("sigprobe",num,value); //
        }
        void setSigSelectedTemplate(int num, int value) {
            cout << "sigselected" << num << " " << value << endl;
            setTemplateParameter("sigselected",num,value); //
        }
        void setBgdProbeTemplate(int num, int value) {
            cout << "bgdprobe" << num << " " << value << endl;
            setTemplateParameter("bgdprobe",num,value); //
        }
        void setBgdSelectedTemplate(int num, int value) {
            cout << "bgdselected" << num << " " << value << endl;
            setTemplateParameter("bgdselected",num,value); //
        }
        void setSigTemplFileName(string value) {
            setParameter("sigfilename",value);
        }
        void setBgdTemplFileName(string value) {
            setParameter("bgdfilename",value);
        }
        void setDataFileName(string value) {
            setParameter("datafilename",value);
        }
        void setMCTemplFileName(string value) {
            setParameter("mcfilename",value);
        }
        void setDataProbe(int value) {
            setParameter("dataprobe",value);
        }
        void setDataSelected(int value) {
            setParameter("dataselected",value);
        }
        void setEfficiency(double value) {
            setParameter("efficiency",value);
        }
        void setEfficiencyError(double value) {
            setParameter("efficiency_err",value);
        }
};

#endif
